﻿//===============================================================================
// Microsoft Innovation Centre - Western Europe
// Copyright (c) 2008 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Practices.Composite.Events;
using Microsoft.WVB.Silverlight.Infrastructure.Events;

namespace Microsoft.WVB.Silverlight.Infrastructure
{
    public class DIContainer : IDisposable, IDIContainer
    {
        #region Object Creation Registration
        public delegate object Creator(DIContainer container);
        private readonly Dictionary<Type, Creator> _typeCreator = new Dictionary<Type, Creator>();
        public void Register<T>(Creator creator)
        {
            _typeCreator.Add(typeof(T), creator);
        }

        public T Create<T>()
        {
            object objCreated = _typeCreator[typeof(T)](this);
            T t = (T)objCreated;
            InjectServiceDependencies(objCreated);
            RegisterEventHandlers(objCreated);
            return t;
        }
        public object CreateObj(string type)
        {
            Type t = Type.GetType(type);
            object objCreated = _typeCreator[t](this);
            InjectServiceDependencies(objCreated);
            RegisterEventHandlers(objCreated);
            return objCreated;
        }
        private void InjectServiceDependencies(object objCreated)
        {
            //Support for [ServiceDependency] custom attribute
            foreach (var gpi in objCreated.GetType().GetProperties())
            {
                object[] dependencies = gpi.GetCustomAttributes(typeof(ServiceDependencyAttribute), false);
                if (null == dependencies) continue;
                foreach (ServiceDependencyAttribute sda in dependencies)
                {
                    if (ServiceDependencyInstancingMode.CreateNew == sda.ServiceDependencyInstancingMode)
                    {
                        MethodInfo rMethod = this.GetType().GetMethod("Create");
                        MethodInfo rGenericMethod = rMethod.MakeGenericMethod(gpi.PropertyType);
                        object o = rGenericMethod.Invoke(this, null);
                        gpi.SetValue(objCreated, o, null);
                    }
                    if (ServiceDependencyInstancingMode.Share == sda.ServiceDependencyInstancingMode)
                    {
                        MethodInfo rMethod = this.GetType().GetMethod("GetCreate");
                        MethodInfo rGenericMethod = rMethod.MakeGenericMethod(gpi.PropertyType);
                        object o = rGenericMethod.Invoke(this, null);
                        gpi.SetValue(objCreated, o, null);
                    }
                }
            }
        }

        private void RegisterEventHandlers(object objCreated)
        {
            IEventAggregator aggr = IoC.Get<IEventAggregator>();
            if (null == aggr) return;
            //Support for [NotificationEventHandlerAttribute] custom attribute
            foreach (var mi in objCreated.GetType().GetMethods())
            {
                object[] handlers = mi.GetCustomAttributes(typeof(NotificationEventHandlerAttribute), false);
                if (null == handlers) continue;
                if (handlers.Length == 0) continue;
                foreach (NotificationEventHandlerAttribute meh in handlers)
                {
                    MethodInfo mi2 = objCreated.GetType().GetMethod(mi.Name);
                    //Type argumentType = mi2.GetParameters()[0].ParameterType;
                    CompositeSilverlightEvent<TopicDataEventArgs<Object>> evt = 
                        aggr.GetEvent<CompositeSilverlightEvent<TopicDataEventArgs<Object>>>();
                    
                    evt.Subscribe(
                        o=>mi2.Invoke(objCreated,new object[]{o.Value}),
                        meh.HandlerThreadOption,
                        true,
                        x=>x.Topic == meh.Topic
                    );
                }
            }
        }
        //private void RegisterEventHandlers(object objCreated)
        //{
        //    return;
        //    //IMediator mediator = null;
        //    ////Support for [MediatorEventHandlerAttribute] custom attribute
        //    //foreach (var mi in objCreated.GetType().GetMethods())
        //    //{
        //    //    object[] handlers = mi.GetCustomAttributes(typeof(MediatorEventHandlerAttribute), false);
        //    //    if (null == handlers) continue;
        //    //    if (handlers.Length == 0) continue;
        //    //    //This requires IoC to have registrered a global mediator
        //    //    if (null == mediator)
        //    //        mediator = IoC.CurrentApplicationMediator;

        //    //    foreach (MediatorEventHandlerAttribute meh in handlers)
        //    //    {
        //    //        MethodInfo mi2 = objCreated.GetType().GetMethod(mi.Name);
        //    //        ProxyColleague prx = new ProxyColleague(mediator, (args) => mi2.Invoke(objCreated, new object[] { args }));
        //    //        //ProxyColleague prx = new ProxyColleague(mediator, (e) => mi.Invoke(objCreated, new object[] { e }));
        //    //        mediator.Register(prx, new string[] { meh.Topic });
        //    //    }
        //    //}
        //}
        #endregion

        #region Instance Handling
        private Dictionary<Type, object> _typeInstance = new Dictionary<Type, object>();
        public T GetCreate<T>()
        {
            Type Ttype = typeof(T);
            if (!_typeInstance.ContainsKey(Ttype))
            {
                _typeInstance[Ttype] = Create<T>();
            }
            return (T)_typeInstance[Ttype];
        }
        public void CreateStore<T>()
        {
            Type Ttype = typeof(T);
            if (!_typeInstance.ContainsKey(Ttype))
            {
                _typeInstance[Ttype] = Create<T>();
            }
            else {
                throw new ArgumentException("Already created");
            }
        }
        public T Get<T>()
        {
            Type Ttype = typeof(T);
            if (!_typeInstance.ContainsKey(Ttype))
            {
                throw new NullReferenceException(string.Format("{0} type is not registrered", Ttype.ToString()));
            }
            return (T)_typeInstance[Ttype];
        }
        public void RemoveInstance<T>()
        {
            Type Ttype = typeof(T);
            if (_typeInstance.ContainsKey(Ttype))
            {
                if (_typeInstance[Ttype] is IDisposable)
                    ((IDisposable)_typeInstance[Ttype]).Dispose();
            }
            _typeInstance.Remove(Ttype);
        }
        public void RegisterInstance<T>(T t)
        {
            Type Ttype = typeof(T);
            _typeInstance[Ttype] = t;
        }
        #endregion

        #region Configuration

        private readonly Dictionary<string, object> _configuration = new Dictionary<string, object>();

        public Dictionary<string, object> Configuration
        {
            get { return _configuration; }
        }
        public T GetNamedConfigurationItem<T>(string name)
        {
            return (T)_configuration[name];
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            foreach (var item in _typeInstance.Values)
            {
                if (item is IDisposable)
                {
                    ((IDisposable)item).Dispose();
                }
            }
            _typeInstance.Clear();
        }

        #endregion

    }
}
